Skip to content

Conversation

prooma
Copy link
Contributor

@prooma prooma commented Sep 20, 2025

Description of Change / Summary

This PR makes TLS writes robust against partial mbedtls_ssl_write() returns and eliminates zero-length writes. It also sends large payloads in 4 KB chunks which significantly reduces MBEDTLS_ERR_NET_CONN_RESET (−0x0050) occurrences on long HTTP bodies (e.g. multipart uploads).

Motivation / Problem

In send_ssl_data() we currently call mbedtls_ssl_write(ctx, data, len) once and return as soon as it returns >0. This means we may send less than len bytes and rely on higher layers to retry. Many callers (including WiFiClientSecure::write) then return that partial size to user code, which can break long HTTP requests.
Additionally, zero-length writes were not guarded; we observed “Writing … 0 bytes” followed by peer resets during multipart uploads.

Fix

  • Loop until the entire buffer is written; handle WANT_READ/WRITE correctly.
  • Guard zero-length calls (len==0 -> return 0).
  • Chunk large writes (4 KB), which proved more reliable with mbedTLS + some servers and access points.

Behavioral changes

  • No API changes. WiFiClientSecure::write() still returns the number of bytes written, now reliably == size on success.
  • Failure paths and timeouts remain unchanged.

Test Scenarios

  • Reproduced with a Telegram Bot multipart sendPhoto upload. Before: frequent (-0x0050) MBEDTLS_ERR_NET_CONN_RESET mid-body, sometimes preceded by a 0-byte write; after patch: stable uploads.
  • Verified on ESP32-S3; TLS handshake and certificate verification unaffected (same logs as before).
  • Also verified small payloads and GET requests.

Why chunking?

Some setups are sensitive to very large TLS records and single-shot writes. Chunking to 4 KB reduces resets without measurable throughput loss and keeps memory usage predictable.

Alternatives

  • Make chunk size configurable; we kept 4 KB static for simplicity.
  • Do the loop in WiFiClientSecure::write() instead—this would duplicate logic; it’s more coherent in ssl_client.cpp.

Risks

  • Low. The change only affects the write path; handshake/verify/reads are untouched.

Logs (before / after)

  • Before: header then “Writing HTTP request with 0 bytes…” → (-80) UNKNOWN ERROR CODE (0050) (peer reset).
  • After: multiple 4 KB writes; no resets; server returns 200 with JSON body.

@prooma prooma requested a review from a team as a code owner September 20, 2025 21:47
@CLAassistant
Copy link

CLAassistant commented Sep 20, 2025

CLA assistant check
All committers have signed the CLA.

Copy link
Contributor

github-actions bot commented Sep 20, 2025

Messages
📖 🎉 Good Job! All checks are passing!

👋 Hello prooma, we appreciate your contribution to this project!


📘 Please review the project's Contributions Guide for key guidelines on code, documentation, testing, and more.

🖊️ Please also make sure you have read and signed the Contributor License Agreement for this project.

Click to see more instructions ...


This automated output is generated by the PR linter DangerJS, which checks if your Pull Request meets the project's requirements and helps you fix potential issues.

DangerJS is triggered with each push event to a Pull Request and modify the contents of this comment.

Please consider the following:
- Danger mainly focuses on the PR structure and formatting and can't understand the meaning behind your code or changes.
- Danger is not a substitute for human code reviews; it's still important to request a code review from your colleagues.
- To manually retry these Danger checks, please navigate to the Actions tab and re-run last Danger workflow.

Review and merge process you can expect ...


We do welcome contributions in the form of bug reports, feature requests and pull requests.

1. An internal issue has been created for the PR, we assign it to the relevant engineer.
2. They review the PR and either approve it or ask you for changes or clarifications.
3. Once the GitHub PR is approved we do the final review, collect approvals from core owners and make sure all the automated tests are passing.
- At this point we may do some adjustments to the proposed change, or extend it by adding tests or documentation.
4. If the change is approved and passes the tests it is merged into the default branch.

Generated by 🚫 dangerJS against c78c814

Loop in send_ssl_data() until the entire buffer is written;
handle MBEDTLS_ERR_SSL_WANT_{READ,WRITE} and respect socket timeouts.
Return 0 for len==0 to prevent zero-length TLS writes.
Add a size==0 guard in WiFiClientSecure::write() for symmetry.

No API changes.
Chunk TLS writes and reset timeout after progress to reduce mid-body resets

Send large TLS payloads in moderate chunks (4 KiB) instead of a single large write,
and measure the write timeout from the last successful progress. This significantly
reduces sporadic MBEDTLS_ERR_NET_CONN_RESET (-0x0050) observed during long HTTP bodies
(e.g., multipart uploads).

- write loop remains intact; now caps per-call size to 4096 bytes
- updates timeout window after each positive write to avoid false timeouts on slow links
- no API changes; handshake/verification paths unaffected

Sources

Ask ChatGPT
@prooma prooma force-pushed the fix/chunked-tls-write branch from a81b3fb to 010e6a0 Compare September 20, 2025 22:00
@lucasssvaz
Copy link
Member

@me-no-dev PTAL

Copy link
Contributor

github-actions bot commented Sep 20, 2025

Test Results

 76 files   76 suites   14m 25s ⏱️
 38 tests  38 ✅ 0 💤 0 ❌
241 runs  241 ✅ 0 💤 0 ❌

Results for commit c78c814.

♻️ This comment has been updated with latest results.

@me-no-dev me-no-dev requested a review from Copilot September 20, 2025 22:29
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR fixes robustness issues in TLS writes for WiFiClientSecure by implementing complete buffer transmission, guarding against zero-length writes, and chunking large payloads to prevent connection resets.

  • Refactors send_ssl_data() to loop until the entire buffer is written rather than returning on partial writes
  • Adds zero-length write guards in both send_ssl_data() and NetworkClientSecure::write()
  • Implements 4KB chunking for large writes to reduce MBEDTLS_ERR_NET_CONN_RESET errors

Reviewed Changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
ssl_client.cpp Implements robust write loop with chunking and zero-length guards
NetworkClientSecure.cpp Adds zero-length write guard at API level

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Copy link
Contributor

Memory usage test (comparing PR against master branch)

The table below shows the summary of memory usage change (decrease - increase) in bytes and percentage for each target.

MemoryFLASH [bytes]FLASH [%]RAM [bytes]RAM [%]
TargetDECINCDECINCDECINCDECINC
ESP32C50⚠️ +460.000.00000.000.00
ESP32P40⚠️ +460.000.00000.000.00
ESP32S30⚠️ +600.00⚠️ +0.01000.000.00
ESP32S20⚠️ +640.00⚠️ +0.01000.000.00
ESP32C30⚠️ +460.000.00000.000.00
ESP32C60⚠️ +460.000.00000.000.00
ESP320⚠️ +640.00⚠️ +0.01000.000.00
Click to expand the detailed deltas report [usage change in BYTES]
TargetESP32C5ESP32P4ESP32S3ESP32S2ESP32C3ESP32C6ESP32
ExampleFLASHRAMFLASHRAMFLASHRAMFLASHRAMFLASHRAMFLASHRAMFLASHRAM
libraries/HTTPClient/examples/Authorization⚠️ +460⚠️ +460⚠️ +520⚠️ +560⚠️ +460⚠️ +460⚠️ +560
libraries/HTTPClient/examples/BasicHttpClient⚠️ +460⚠️ +460⚠️ +520⚠️ +560⚠️ +460⚠️ +460⚠️ +560
libraries/HTTPClient/examples/BasicHttpsClient⚠️ +460⚠️ +460⚠️ +520⚠️ +480⚠️ +460⚠️ +460⚠️ +560
libraries/HTTPClient/examples/CustomHeaders⚠️ +460⚠️ +460⚠️ +520⚠️ +560⚠️ +460⚠️ +460⚠️ +560
libraries/HTTPClient/examples/HTTPClientEnterprise⚠️ +460--⚠️ +520⚠️ +560⚠️ +460⚠️ +460⚠️ +560
libraries/HTTPClient/examples/ReuseConnection⚠️ +460⚠️ +460⚠️ +520⚠️ +640⚠️ +460⚠️ +460⚠️ +560
libraries/HTTPClient/examples/StreamHttpClient⚠️ +460⚠️ +460⚠️ +520⚠️ +560⚠️ +460⚠️ +460⚠️ +600
libraries/HTTPUpdate/examples/httpUpdate00000000000000
libraries/HTTPUpdate/examples/httpUpdateSPIFFS00000000000000
libraries/HTTPUpdate/examples/httpUpdateSecure⚠️ +460⚠️ +460⚠️ +560⚠️ +560⚠️ +460⚠️ +460⚠️ +560
libraries/NetworkClientSecure/examples/WiFiClientInsecure⚠️ +460⚠️ +460⚠️ +560⚠️ +560⚠️ +460⚠️ +460⚠️ +640
libraries/NetworkClientSecure/examples/WiFiClientPSK⚠️ +460⚠️ +460⚠️ +520⚠️ +560⚠️ +460⚠️ +460⚠️ +560
libraries/NetworkClientSecure/examples/WiFiClientSecure⚠️ +460⚠️ +460⚠️ +520⚠️ +560⚠️ +460⚠️ +460⚠️ +560
libraries/NetworkClientSecure/examples/WiFiClientSecureEnterprise⚠️ +460--⚠️ +520⚠️ +560⚠️ +460⚠️ +460⚠️ +560
libraries/NetworkClientSecure/examples/WiFiClientSecureProtocolUpgrade⚠️ +460⚠️ +460⚠️ +560⚠️ +480⚠️ +460⚠️ +460⚠️ +560
libraries/NetworkClientSecure/examples/WiFiClientShowPeerCredentials⚠️ +460⚠️ +460⚠️ +520⚠️ +560⚠️ +460⚠️ +460⚠️ +600
libraries/NetworkClientSecure/examples/WiFiClientTrustOnFirstUse⚠️ +460⚠️ +460⚠️ +600⚠️ +640⚠️ +460⚠️ +460⚠️ +600
libraries/Update/examples/HTTP_Client_AES_OTA_Update00000000000000
libraries/WiFi/examples/WiFiMultiAdvanced⚠️ +460⚠️ +460⚠️ +520⚠️ +560⚠️ +460⚠️ +460⚠️ +360

@me-no-dev me-no-dev added the Status: Pending Merge Pull Request is ready to be merged label Sep 23, 2025
@me-no-dev me-no-dev merged commit f4f4bc6 into espressif:master Sep 24, 2025
70 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Status: Pending Merge Pull Request is ready to be merged
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants